#ifndef GVL_IO_STREAM_HPP
#define GVL_IO_STREAM_HPP

#include <stdlib.h>
#include <stddef.h>

#include "../resman/shared.h"
#include "../containers/list.h"

#define GVL_STREAM_OK (0)
#define GVL_STREAM_WOULD_BLOCK (1)
#define GVL_STREAM_EOF (2)
#define GVL_STREAM_ERROR (3)

typedef struct gvl_bucket_data_
{
	gvl_shared shared;
	gvl_list_node list_node;
	
} gvl_bucket_data;

typedef struct gvl_bucket_data_mem_
{
	gvl_bucket_data base;
	unsigned len;
	unsigned char data[1];
	
} gvl_bucket_data_mem;

#define GVL_BUCKET_DATA_MEM_SIZE(len) (sizeof(gvl_bucket_data_mem)+(len)-1)


typedef struct gvl_bucket_
{
	gvl_bucket_data* data;
	unsigned offset, length;
} gvl_bucket;

typedef struct gvl_read_result_
{
	int result;
	gvl_bucket* bucket;
} gvl_read_result;

typedef gvl_read_result (*gvl_stream_get_t)(struct gvl_stream_* self);
typedef int             (*gvl_stream_put_t)(struct gvl_stream_* self, gvl_bucket* bucket);

typedef struct gvl_stream_
{
	gvl_stream_get_t get;
	gvl_stream_put_t put;
	
	unsigned initial_bucket_len;
} gvl_stream;

typedef struct gvl_byte_writer_
{
	unsigned char* next;
	unsigned char* limit;
	gvl_bucket_data_mem* cur_bucket;
	
	gvl_stream* sink;
} gvl_byte_writer;

int gvl_write_byte_overflow_(gvl_byte_writer* self, unsigned char byte);

GVL_INLINE int gvl_stream_put(gvl_stream* self, gvl_bucket* bucket)
{
	// TODO: If the put fails, buffer it
	return self->put(self, bucket);
}

GVL_INLINE gvl_read_result gvl_stream_get(gvl_stream* self, gvl_bucket* bucket)
{
	// TODO: Get from buffered buckets first
	return self->get(self);
}

GVL_INLINE void gvl_bucket_data_init(gvl_bucket_data* self, void (*freer)(void*))
{
	gvl_shared_init(&self->shared, freer);
}

void gvl_bucket_data_mem_free(void* self_)
{
	gvl_bucket_data_mem* self = self_;
	free(self);
}

GVL_INLINE gvl_bucket_data_mem*
gvl_bucket_data_mem_new(unsigned len)
{
	gvl_bucket_data_mem* data = malloc(GVL_BUCKET_DATA_MEM_SIZE(len));
	
	gvl_bucket_data_init(&data->base, gvl_bucket_data_mem_free);
	data->len = len;
	return data;
}

GVL_INLINE void gvl_bucket_init(gvl_bucket* self, unsigned offset, unsigned len, gvl_bucket_data* data)
{
	self->data = data;
	self->offset = offset;
	self->length = len;
}

GVL_INLINE gvl_bucket* gvl_bucket_new(unsigned offset, unsigned len, gvl_bucket_data* data)
{
	gvl_bucket* self = malloc(sizeof(gvl_bucket));
	gvl_bucket_init(self, offset, len, data);
	return self;
}

GVL_INLINE void gvl_stream_init(gvl_stream* self, gvl_stream_get_t get, gvl_stream_put_t put)
{
	self->get = get;
	self->put = put;
	self->initial_bucket_len = 128;
}

GVL_INLINE void gvl_byte_writer_init(gvl_byte_writer* self, gvl_stream* sink)
{
	self->next = NULL;
	self->limit = NULL;
	self->cur_bucket = NULL;
	self->sink = sink;
}

GVL_INLINE int gvl_write_byte(gvl_byte_writer* self, unsigned char byte)
{
	if(self->next == self->limit)
		return gvl_write_byte_overflow_(self, byte);
		
	*self->next++ = byte;
	return 0;
}

#endif // GVL_IO_STREAM_HPP
